package cc.mrbird.febs.common.datasource.starter.organization;

import com.xf.common.core.enums.YesOrNoEnum;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.util.function.Supplier;

@Slf4j
@UtilityClass
public class OrganizationBroker {

	@FunctionalInterface
	public interface RunAs<T> {

		/**
		 * 执行业务逻辑
		 * @param organizationId
		 * @throws Exception
		 */
		void run(T organizationId) throws Exception;

	}

	@FunctionalInterface
	public interface ApplyAs<T, R> {

		/**
		 * 执行业务逻辑,返回一个值
		 * @param organizationId
		 * @return
		 * @throws Exception
		 */
		R apply(T organizationId) throws Exception;

	}

	@FunctionalInterface
	public interface NoRun {

		/**
		 * 执行业务逻辑
		 * @throws Exception
		 */
		void run() throws Exception;

	}

	@FunctionalInterface
	public interface NoApplyAs<R> {

		/**
		 * 执行业务逻辑,返回一个值
		 * @return
		 * @throws Exception
		 */
		R apply() throws Exception;

	}

	/**
	 * 以某个租户的身份运行
	 * @param organizationId 租户ID
	 * @param func
	 */
	public void runAs(String organizationId, RunAs<String> func) {
		final OrganizationContextHolder.OrganizationConfig organizationConfig = OrganizationContextHolder.getOrganizationConfig();

		try {
			if (organizationId.equals(YesOrNoEnum.No.getValue())) {
				organizationId = null;
			}
			//log.debug("OrganizationBroker 切换租户{} -> {}", organizationConfig.getOrganizationId(), organizationId);
			OrganizationContextHolder.setOrganizationConfig(organizationId, true);
			func.run(organizationId);
		}
		catch (Exception e) {
			log.debug(e.getMessage(), e);
			throw new OrganizationBrokerExceptionWrapper(e.getMessage(), e);
		}
		finally {
			//log.debug("OrganizationBroker 还原租户{} <- {}", organizationConfig, organizationId);
			OrganizationContextHolder.setOrganizationConfig(organizationConfig.getOrganizationId(), organizationConfig.getOrganizationFlag());
		}
	}

	/**
	 * 以某个租户的身份运行
	 * @param organizationId 租户ID
	 * @param func
	 * @param <T> 返回数据类型
	 * @return
	 */
	public <T> T applyAs(String organizationId, ApplyAs<String, T> func) {
		final OrganizationContextHolder.OrganizationConfig organizationConfig = OrganizationContextHolder.getOrganizationConfig();
		try {
			if (organizationId.equals(YesOrNoEnum.No.getValue())) {
				organizationId = null;
			}
			log.debug("OrganizationBroker 切换租户{} -> {}", organizationConfig.getOrganizationId(), organizationId);
			OrganizationContextHolder.setOrganizationConfig(organizationId, true);
			return func.apply(organizationId);
		}
		catch (Exception e) {
			log.debug(e.getMessage(), e);
			throw new OrganizationBrokerExceptionWrapper(e.getMessage(), e);
		}
		finally {
			log.debug("OrganizationBroker 还原租户{} <- {}", organizationConfig.getOrganizationId(), organizationId);
			OrganizationContextHolder.setOrganizationConfig(organizationConfig.getOrganizationId(), organizationConfig.getOrganizationFlag());
		}
	}

	/**
	 * 以某个租户的身份运行
	 * @param supplier
	 * @param func
	 */
	public void runAs(Supplier<String> supplier, RunAs<String> func) {
		runAs(supplier.get(), func);
	}

	/**
	 * 以某个租户的身份运行
	 * @param supplier
	 * @param func
	 * @param <T> 返回数据类型
	 * @return
	 */
	public <T> T applyAs(Supplier<String> supplier, ApplyAs<String, T> func) {
		return applyAs(supplier.get(), func);
	}

	/**
	 * 不以某个租户的身份运行
	 * @param noRun
	 */
	public void noRunAs(NoRun noRun) {
		final OrganizationContextHolder.OrganizationConfig organizationConfig = OrganizationContextHolder.getOrganizationConfig();
		try {
			log.debug("OrganizationBroker 不以租户的身份运行");
			OrganizationContextHolder.setOrganizationConfig(null, false);
			noRun.run();
		}
		catch (Exception e) {
			log.debug(e.getMessage(), e);
			throw new OrganizationBrokerExceptionWrapper(e.getMessage(), e);
		}
		finally {
			if (organizationConfig != null) {
				log.debug("OrganizationBroker 还原租户{} <- {}", organizationConfig.getOrganizationId(), organizationConfig.getOrganizationId());
				OrganizationContextHolder.setOrganizationConfig(organizationConfig.getOrganizationId(), organizationConfig.getOrganizationFlag());
			}
		}
	}

	/**
	 * 不以租户的身份运行
	 * @param noApplyAs
	 * @param <T> 返回数据类型
	 * @return
	 */
	public <T> T noApplyAs(NoApplyAs<T> noApplyAs) {
		final OrganizationContextHolder.OrganizationConfig organizationConfig = OrganizationContextHolder.getOrganizationConfig();
		try {
			log.debug("OrganizationBroker 不以租户的身份运行");
			OrganizationContextHolder.setOrganizationConfig(null, false);
			return noApplyAs.apply();
		}
		catch (Exception e) {
			log.debug(e.getMessage(), e);
			throw new OrganizationBrokerExceptionWrapper(e.getMessage(), e);
		}
		finally {
			if (organizationConfig != null) {
				log.debug("OrganizationBroker 还原租户{} <- {}", organizationConfig.getOrganizationId(), organizationConfig.getOrganizationId());
				OrganizationContextHolder.setOrganizationConfig(organizationConfig.getOrganizationId(), organizationConfig.getOrganizationFlag());
			}
		}
	}

	public static class OrganizationBrokerExceptionWrapper extends RuntimeException {

		public OrganizationBrokerExceptionWrapper(String message, Throwable cause) {
			super(message, cause);
		}

		public OrganizationBrokerExceptionWrapper(Throwable cause) {
			super(cause);
		}

	}

}
